package hxdb

import (
  "strconv"
  "strings"
)

// define table interface
type TableInterface interface {
  Init(table string)
  FindAll() ([]map[string]interface{}, error)
  FindOne() (map[string]interface{}, error)
  Count() (int64, error)
  Insert(data map[string]interface{}) (int64, error)
  Update(data map[string]interface{}) (int64, error)
  Delete() (int64, error)
}

// db default table struct
type Table struct {
  db         DbInterface
  tableName  string
  rows       string
  sqlBuilder *Sql
  sqlArgs    []interface{}
}

// create New Table object from a db connection
func NewTable(tableName string, db DbInterface) *Table {
  t := &Table{}
  t.Init(tableName)
  t.SetDb(db)
  return t
}

// set db object to table
func (this *Table) SetDb(db DbInterface) *Table {
  this.db = db
  return this
}

// set table name
func (this *Table) SetTable(tbl string) *Table {
  this.tableName = tbl
  return this
}

// init table object with table_name
func (this *Table) Init(table string) {
  this.tableName = table
  this.rows = "*"
  this.sqlBuilder = NewSql(table, this.rows)
  this.sqlArgs = make([]interface{}, 0)
}

// set where condition, allow placeholder params
func (this *Table) Where(str string, args ...interface{}) *Table {
  this.sqlBuilder.Where(str)
  if len(args) > 0 {
    for _, arg := range args {
      this.sqlArgs = append(this.sqlArgs, arg)
    }
  }
  return this
}

// set order condition, not allow placeholder params
func (this *Table) Order(str string) *Table {
  this.sqlBuilder.Order(str)
  return this
}

// set limit condition, not allow placeholder params
func (this *Table) Limit(num int) *Table {
  this.sqlBuilder.Limit(num, 0)
  return this
}

// set pager division, not allow placeholder params
func (this *Table) Page(page int, size int) *Table {
  this.sqlBuilder.Page(page, size)
  return this
}

// set custom columns
func (this *Table) Column(col string) *Table {
  this.sqlBuilder.Column = col
  return this
}

// gets all data
func (this *Table) FindAll() ([]map[string]interface{}, error) {
  sql := this.sqlBuilder.Select()
  args := this.sqlArgs
  this.Init(this.tableName)
  return this.db.Query(sql, args...)
}

// get one data
func (this *Table) FindOne() (map[string]interface{}, error) {
  sql := this.sqlBuilder.Select()
  args := this.sqlArgs
  this.Init(this.tableName)
  return this.db.One(sql, args...)
}

// get count number
func (this *Table) Count() (int64, error) {
  sql := this.sqlBuilder.Count()
  res, err := this.db.One(sql)
  if err != nil {
    return -1, err
  }
  return strconv.ParseInt(res["countNum"].(string), 10, 64)
}

// insert data, return last id
func (this *Table) Insert(data map[string]interface{}) (int64, error) {
  cols := make([]string, 0)
  args := make([]interface{}, 0)
  for k, v := range data {
    cols = append(cols, k)
    args = append(args, v)
  }
  if len(this.sqlArgs) > 0 {
    for _, arg := range this.sqlArgs {
      args = append(args, arg)
    }
  }
  this.sqlBuilder.Column = strings.Join(cols, ",")
  sql := this.sqlBuilder.Insert()
  this.Init(this.tableName)
  rs, err := this.db.Exec(sql, args...)
  if err != nil {
    return -1, err
  }
  return rs.LastInsertId()
}

// update data, return affected rows
func (this *Table) Update(data map[string]interface{}) (int64, error) {
  cols := make([]string, 0)
  args := make([]interface{}, 0)
  for k, v := range data {
    cols = append(cols, k)
    args = append(args, v)
  }
  if len(this.sqlArgs) > 0 {
    for _, arg := range this.sqlArgs {
      args = append(args, arg)
    }
  }
  this.sqlBuilder.Column = strings.Join(cols, ",")
  sql := this.sqlBuilder.Update()
  this.Init(this.tableName)
  rs, err := this.db.Exec(sql, args...)
  if err != nil {
    return -1, err
  }
  return rs.RowsAffected()
}

// delete data, return affected rows
func (this *Table) Delete() (int64, error) {
  sql := this.sqlBuilder.Delete()
  args := this.sqlArgs
  this.Init(this.tableName)
  rs, err := this.db.Exec(sql, args...)
  if err != nil {
    return -1, err
  }
  return rs.RowsAffected()
}
